package match

import (
	"time"
	"sync"
	"fmt"

	"gitee.com/wint/tge/worker"
)

var _pring = fmt.Println

// 匹配池
type MatchPool struct {
	sync.Mutex	// 线程锁

	MatchCallback 	func([][]int32) // 匹配成功回调，参数为队伍列表（每个队伍可能有多个玩家）

	CancelCallback	func([]int32)	// 取消匹配回调

	TimeoutCallback func([]int32) 	// 超时回调

	// 队伍参数，TeamCount = 2, TeamPlayerNum = 1 时表示这是 1v1 匹配
	TeamCount	int 	// 对战的队伍数量，3及3以上为多队伍混战
	TeamPlayerNum	int	// 每个队伍的玩家数量

	Players		map[int32]*MatchPlayer 	// 等待匹配的玩家列表

	Rooms 		[]*MatchRoom	// 按积分分段后的房间列表

	RatingSplit int32 	// 积分的分段基数，默认 50
	ExpandDt	int 	// 匹配不到人的扩大匹配时间，默认 5 秒
	MaxDis		int32 	// 最大匹配距离，限制分数差距太大的玩家不能匹配，默认是10

	Timeout 	int 	// 匹配超时时间，0 为不超时，一直会进行匹配

	Interval 	time.Duration	// 匹配的刷新间隔，默认 0.1 秒刷新一次

	closeSig	chan bool

	isStart 	bool
}

func NewMatchPool() *MatchPool {
	pool := &MatchPool{}

	pool.TeamCount = 2
	pool.TeamPlayerNum = 1

	pool.Players = make(map[int32]*MatchPlayer)
	pool.Rooms = make([]*MatchRoom, 0, 500)

	pool.RatingSplit = 50
	pool.ExpandDt = 5
	pool.MaxDis = 10

	pool.Timeout = 300

	pool.Interval = 100 * time.Millisecond

	pool.closeSig = make(chan bool, 1)
	pool.isStart = false

	return pool
}

func (m *MatchPool) Run() {
	if m.isStart {
		return
	}
	m.isStart = true
	go func() {
		t := time.NewTicker(m.Interval)
		defer t.Stop()
		for {
			select {
			case <-t.C:
				m.Update(m.Interval)
			case <-m.closeSig:
				m.isStart = false
				return
			}
		}
	}()
}

func (m *MatchPool) Stop() {
	if !m.isStart {
		return
	}
	m.closeSig <- true
}

func (m *MatchPool) GetRoomId(rating int32) int32 {
	if rating < 0 {
		return 0
	}
	if m.RatingSplit > 0 {
		return rating / m.RatingSplit
	}
	return rating / 50
}

func (m *MatchPool) IsInMatching(playerId int32) bool {
	m.Lock()
	defer m.Unlock()

	if _, ok := m.Players[playerId]; ok {
		return true
	}
	return false
}

// 加入匹配，playerId, rating, ...
func (m *MatchPool) AddPlayer(infos ... int32) bool {
	m.Lock()
	defer m.Unlock()

	if len(infos) / 2 > m.TeamPlayerNum {
		// fmt.Println("加入的人数起过队伍上限", infos, m.TeamPlayerNum)
		return false
	}

	for i := 0; i < len(infos) - 1; i += 2 {
		if _, ok := m.Players[infos[i]]; ok {
			// fmt.Println("已经有队员在匹配中", infos, infos[i])
			return false;
		}
	}
	p := NewMatchPlayer(infos...)
	roomId := m.GetRoomId(p.Rating)
	n := int32(len(m.Rooms))
	if roomId >= n {
		for i := n; i <= roomId; i++ {
			m.Rooms = append(m.Rooms, NewMatchRoom(i, m))
		}
	}
	m.Rooms[roomId].AddPlayer(p)
	for _, pid := range p.PlayerIds {
		m.Players[pid] = p
	}
	return true
}

// 退出匹配
func (m *MatchPool) RemovePlayer(playerId int32) {
	m.Lock()
	defer m.Unlock()

	if p, ok := m.Players[playerId]; ok {
		m._removePlayer(p)

		m.onCancel(p)
	}
}

func (m *MatchPool) _removePlayer(p *MatchPlayer) {
	if len(p.PlayerIds) > 0 {
		if p, ok := m.Players[p.PlayerIds[0]]; ok {
			for _, pid := range p.PlayerIds {
				delete(m.Players, pid)
			}
			if nil != p.Team {
				roomId := m.GetRoomId(p.Team.Rating)
				if roomId >= 0 && roomId < int32(len(m.Rooms)) {
					m.Rooms[roomId].RemoveFromTeam(p)
				}
			} else {
				roomId := m.GetRoomId(p.Rating)
				if roomId >= 0 && roomId < int32(len(m.Rooms)) {
					m.Rooms[roomId].RemovePlayer(p)
				}
			}
		}
	}
}

func (m *MatchPool) _removePlayerFromTeam(t *MatchTeam) {
	for _, p := range t.Players {
		for _, pid := range p.PlayerIds {
			delete(m.Players, pid)
		}
	}
}

// 同个房间内的队伍可以匹配
func (m *MatchPool) matchOneRoom(room *MatchRoom) {
	for ;len(room.Teams) >= m.TeamCount; {
		teams := make([]*MatchTeam, 0, m.TeamCount)
		for i := 0; i < m.TeamCount; i++ {
			m._removePlayerFromTeam(room.Teams[i])
			teams = append(teams, room.Teams[i])
		}
		room.Teams = append(room.Teams[:0], room.Teams[m.TeamCount:]...)
		m.onMatch2(teams)
	}
}

func (m *MatchPool) _appendTeams(teams []*MatchTeam, roomId int32) ([]*MatchTeam, bool) {
	if roomId >= 0 && roomId < int32(len(m.Rooms)) {
		for _, v := range m.Rooms[roomId].Teams {
			teams = append(teams, v)
			if len(teams) >= m.TeamCount {
				return teams, true
			}
		}
	}
	return teams, false
}

func (m *MatchPool) _matchDiffRoom(teams []*MatchTeam, roomId int32, roomDis int32) []*MatchTeam {
	if roomDis > 0 {
		var i int32 = 1
		ok := false
		for ; i <= roomDis; i++ {
			if teams, ok = m._appendTeams(teams, roomId - i); ok {
				return teams
			}
			if teams, ok = m._appendTeams(teams, roomId + i); ok {
				return teams
			}
		}
	}
	return teams
}

// 跨房间匹配
func (m *MatchPool) matchDiffRoom(room *MatchRoom) {
	teams := make([]*MatchTeam, 0, m.TeamCount)
	var roomDis int32 = 0
	for _, v := range room.Teams {
		teams = append(teams, v)
		if roomDis < v.RoomDis {
			roomDis = v.RoomDis
		}
	}
	if roomDis > 0 {
		teams = m._matchDiffRoom(teams, room.RoomId, roomDis)
		if len(teams) == m.TeamCount {
			for _, t := range teams {
				rid := m.GetRoomId(t.Rating)
				if rid >= 0 && rid < int32(len(m.Rooms)) {
					m.Rooms[rid].RemoveTeam(t)
				}
				m._removePlayerFromTeam(t)
			}
			m.onMatch2(teams)
		} else {
			// fmt.Println("无法匹配到足够人数", len(teams), len(m.Players), roomDis)
		}
	}
}

func (m *MatchPool) _tryMergeTeam(roomId int32, t *MatchTeam) bool {
	if roomId >= 0 && roomId < int32(len(m.Rooms)) {
		m.Rooms[roomId].MergeTeam(t)
		if t.IsFull() {
			return true
		}
	}
	return false
}

func (m *MatchPool) tryMergeTeam(roomId int32, t *MatchTeam) bool {
	if t.RoomDis <= 0 {
		return false
	}
	var i int32 = 1
	for ; i <= t.RoomDis; i++ {
		if m._tryMergeTeam(roomId - i, t) || m._tryMergeTeam(roomId + i, t) {
			return true
		}
	}
	return false
}

func (m *MatchPool) Update(dt time.Duration) {
	m.Lock()
	defer m.Unlock()

	for _, room := range m.Rooms {
		// room.Sort()

		room.MovePlayersToWaitTeam()

		m.matchOneRoom(room)
	}

	s := dt.Seconds()

	for _, v := range m.Players {
		v.Wait += s
		if m.Timeout > 0 && int(v.Wait) >= m.Timeout {
			m._removePlayer(v)
			m.onTimeout(v)
		}
	}
	// 尝试跨分段合并队伍
	for _, room := range m.Rooms {
		room.OnDt(s)
		for k := 0; k < len(room.WaitTeams); {
			t := room.WaitTeams[k]
			room.MergeTeam(t)
			if !t.IsFull() && t.RoomDis > 0 {
				m.tryMergeTeam(room.RoomId, t)
			}
			if t.IsFull() {
				room.WaitTeams = append(room.WaitTeams[:k], room.WaitTeams[k+1:]...)
				r := room
				roomId := m.GetRoomId(t.Rating)
				if roomId >= 0 && roomId < int32(len(m.Rooms)) {
					r = m.Rooms[roomId]
				}
				r.Teams = append(r.Teams, t)
				// fmt.Println("合并队伍成功！", t)
			} else {
				k++
			}
		}
	}

	// 尝试跨分段匹配
	for _, room := range m.Rooms {
		m.matchOneRoom(room)
		if 0 == len(room.Teams) {
			continue
		}
		m.matchDiffRoom(room)
	}
}

func (m *MatchPool) onMatch(teams... *MatchTeam) {
	m.onMatch2(teams)
}

func (m *MatchPool) onMatch2(teams []*MatchTeam) {
	if nil != m.MatchCallback {
		worker.RunWork(func() {
			ret := make([][]int32, 0, len(teams))
			for _, v := range teams {
				ret = append(ret, v.GetPlayerIds())
			}
			m.MatchCallback(ret)
		})
	}
}

func (m *MatchPool) onCancel(p *MatchPlayer) {
	if nil != m.CancelCallback {
		worker.RunWork(func() {
			m.CancelCallback(p.PlayerIds)
		})
	}
}

func (m *MatchPool) onTimeout(p *MatchPlayer) {
	if nil != m.TimeoutCallback {
		worker.RunWork(func() {
			m.TimeoutCallback(p.PlayerIds)
		})
	}
}